package io.kiki.sba.registry.store.jdbc.repository.impl;


import io.kiki.sba.registry.common.model.console.PersistenceData;
import io.kiki.sba.registry.common.model.console.PersistenceDataBuilder;
import io.kiki.sba.registry.store.api.config.DefaultCommonConfig;
import io.kiki.sba.registry.store.api.meta.ProvideDataRepository;
import io.kiki.sba.registry.store.api.meta.RecoverConfig;
import io.kiki.sba.registry.store.api.meta.RecoverConfigRepository;
import io.kiki.sba.registry.store.jdbc.constant.TableEnum;
import io.kiki.sba.registry.store.jdbc.convertor.ProvideDataDomainConvertor;
import io.kiki.sba.registry.store.jdbc.domain.ProvideDataDomain;
import io.kiki.sba.registry.store.jdbc.mapper.ProvideDataMapper;
import io.kiki.sba.registry.store.jdbc.version.config.BaseConfigRepository;
import io.kiki.sba.registry.util.MathUtils;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static io.kiki.sba.registry.store.jdbc.repository.impl.MetadataMetrics.ProvideData.PROVIDE_DATA_QUERY_COUNTER;
import static io.kiki.sba.registry.store.jdbc.repository.impl.MetadataMetrics.ProvideData.PROVIDE_DATA_UPDATE_COUNTER;


public class ProvideDataJdbcRepository implements ProvideDataRepository, RecoverConfig {

    private static final Logger logger = LoggerFactory.getLogger(ProvideDataJdbcRepository.class);
    private static final Integer batchQuerySize = 1000;
    @Autowired
    protected ProvideDataMapper provideDataMapper;
    @Autowired
    protected DefaultCommonConfig defaultCommonConfig;
    @Autowired
    protected RecoverConfigRepository recoverConfigRepository;
    private Configer configer;

    public ProvideDataJdbcRepository() {
        configer = new Configer();
    }

    @PostConstruct
    public void init() {
        recoverConfigRepository.registerCallback(this);
    }

    @Override
    public boolean put(PersistenceData persistenceData) {
        PROVIDE_DATA_QUERY_COUNTER.inc();

        String dataInfoId = PersistenceDataBuilder.getDataInfoId(persistenceData);
        String clusterId = defaultCommonConfig.getClusterId(tableName(), dataInfoId);

        ProvideDataDomain domain = ProvideDataDomainConvertor.convert2ProvideData(persistenceData, clusterId);
        return configer.put(domain);
    }

    @Override
    public boolean put(PersistenceData persistenceData, long expectVersion) {
        PROVIDE_DATA_QUERY_COUNTER.inc();

        String dataInfoId = PersistenceDataBuilder.getDataInfoId(persistenceData);
        String clusterId = defaultCommonConfig.getClusterId(tableName(), dataInfoId);
        ProvideDataDomain domain = ProvideDataDomainConvertor.convert2ProvideData(persistenceData, clusterId);

        return configer.put(domain, expectVersion);
    }

    @Override
    public PersistenceData get(String key) {
        PROVIDE_DATA_QUERY_COUNTER.inc();
        String clusterId = defaultCommonConfig.getClusterId(tableName(), key);
        return ProvideDataDomainConvertor.convert2PersistenceData(provideDataMapper.query(clusterId, key));
    }

    @Override
    public boolean remove(String key, long version) {
        PROVIDE_DATA_UPDATE_COUNTER.inc();
        String clusterId = defaultCommonConfig.getClusterId(tableName(), key);
        int affect = provideDataMapper.remove(clusterId, key, version);
        if (logger.isInfoEnabled()) {
            logger.info("remove provideData, dataCenter: {}, key: {}, version: {}, affect rows: {}", clusterId, key, version, affect);
        }
        return affect > 0;
    }

    @Override
    public Map<String, PersistenceData> getAll() {

        String clusterId = defaultCommonConfig.getClusterId(tableName());
        Map<String, PersistenceData> responses = getAllByClusterId(clusterId);

        if (defaultCommonConfig.isRecoverCluster()) {
            String recoverClusterId = defaultCommonConfig.getRecoverClusterId();
            Map<String, PersistenceData> recoverConfigMap = getAllByClusterId(recoverClusterId);
            logger.info("load recover config by recoverClusterId:{}, ret:{}", recoverClusterId, recoverConfigMap);
            Set<String> dataInfoIds = recoverConfigRepository.queryKey(tableName());

            if (CollectionUtils.isNotEmpty(dataInfoIds)) {
                for (String dataInfoId : dataInfoIds) {
                    // dependency config
                    responses.put(dataInfoId, recoverConfigMap.get(dataInfoId));
                }
            }
        }
        PROVIDE_DATA_QUERY_COUNTER.inc();
        return responses;
    }

    private Map<String, PersistenceData> getAllByClusterId(String clusterId) {
        int total = provideDataMapper.selectTotalCount(clusterId);
        int round = MathUtils.divideCeil(total, batchQuerySize);
        Map<String, PersistenceData> responses = new HashMap<>(total);
        for (int i = 0; i < round; i++) {
            int start = i * batchQuerySize;
            List<ProvideDataDomain> provideDataDomains = provideDataMapper.queryByPage(clusterId, start, batchQuerySize);
            for (ProvideDataDomain provideDataDomain : provideDataDomains) {
                PersistenceData persistenceData = ProvideDataDomainConvertor.convert2PersistenceData(provideDataDomain);
                responses.put(PersistenceDataBuilder.getDataInfoId(persistenceData), persistenceData);
            }
        }
        return responses;
    }

    @Override
    public String tableName() {
        return TableEnum.provide_data.getTableName();
    }

    @Override
    public boolean afterConfigSet(String key, String recoverClusterId) {
        if (defaultCommonConfig.isRecoverCluster()) {
            return true;
        }
        String clusterId = defaultCommonConfig.getClusterId(tableName());
        ProvideDataDomain data = provideDataMapper.query(clusterId, key);
        ProvideDataDomain recoverData = provideDataMapper.query(recoverClusterId, key);
        if (data != null && recoverData == null) {
            // copy config
            recoverData = new ProvideDataDomain(recoverClusterId, data.getDataKey(), data.getDataValue(), PersistenceDataBuilder.nextVersion());
            provideDataMapper.save(recoverData);
            logger.info("[afterConfigSet]save recover cluster:{}, data:{}", recoverClusterId, recoverData);
        }
        return true;
    }

    class Configer extends BaseConfigRepository<ProvideDataDomain> {
        public Configer() {
            super("ProvideData");
        }

        @Override
        protected ProvideDataDomain queryExistVersion(ProvideDataDomain entry) {
            return provideDataMapper.query(entry.getDataCenter(), entry.getDataKey());
        }

        @Override
        protected long insert(ProvideDataDomain entry) {
            return provideDataMapper.save(entry);
        }

        @Override
        protected int updateWithExpectVersion(ProvideDataDomain entry, long exist) {
            return provideDataMapper.update(entry, exist);
        }
    }
}
